上回我們學習變數、條件控制以及多種迴圈。
現在我們來讓 Rust 程式擁有更高的可讀性,我們將學習函數、結構體與模組。
我們可以使用 fn 關鍵字定義一個函數,當有一部分程式碼重複時,我們就能使用函數,讓程式變得簡短更容易理解。或是想要將特定目的的程式碼分離出來,也可以使用函數。
我們定義一個 add 函數,用來處理相加,有 a 和 b 兩個參數,資料形態是32 bit的整數,回傳一個32 bit的整數。
有些人可能發現, a + b 沒有分號,這不是錯誤,無分號表示回傳這個表達式的值。
fn main() {
let sum = add(5, 3);
println!("5 + 3 = {}", sum);
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
我們可以使用 struct 關鍵字來指定資料形態,用來將高關聯的內容組合在一起,清楚表達各個欄位間的關係。
下面我們宣告一個 User 結構體,有 name、email、password的欄位,可以清楚理解欄位分別儲存了User的名字、電子信箱和密碼。
緊接著是使用 User 結構體建立一個 user1,並寫入各欄位的資訊。
最後,我們輸出剛才建立的 user1每個欄位的內容。
struct User {
name: String,
email: String,
password: String,
}
fn main() {
let user1 = User {
name: String::from("user1"),
email: String::from("user1@a.com"),
password: String::from("12345678"),
};
println!("{} {} {}", user1.name, user1.email, user1.password);
}
enum 關鍵字可以用來表示一組互斥的選擇,適合用來表示狀態或選項。在本文的最後,將展示 enum的強大之處,先用一個簡單的範例呈現 enum的使用方式。
我們定義一個 enum,用來處理方向,方向只會是上下左右其中一種,相當適合使用 enum。可以注意到我們在 enum 前加上 #[derive(Debug)] ,這是因為我們在後面使用到 {:?} 用來輸出 enum 的欄位名稱。
#[derive(Debug)]
enum Direction {
Up,
Down,
Left,
Right,
}
fn main() {
let directions = vec![
Direction::Down,
Direction::Right,
Direction::Up,
Direction::Left,
Direction::Down,
];
for dir in directions {
println!("{:?}", dir);
}
}
接下來,我們將 enum 和 match 一起使用,讓它們更加強大。
enum 除了先前的使用方式,還可以夾帶不同資料形態的資料。
match 取代了許多if,看起來更加簡潔清晰。
這是一個處理API接收到的響應
enum ApiResponse {
Success(String),
ClientError(u16, String),
ServerError(u16),
NetworkError,
}
fn handle_response(response: ApiResponse) -> String {
match response {
ApiResponse::Success(data) => {
format!("成功,{}", data)
}
ApiResponse::ClientError(code, msg) if code == 404 => {
format!("找不到資源:{}", msg)
}
ApiResponse::ClientError(code, msg) => {
format!("用戶端錯誤 {}:{}", code, msg)
}
ApiResponse::ServerError(code) => {
format!("伺服器錯誤:{}", code)
}
ApiResponse::NetworkError => {
"連線失敗,請檢查網路設定".to_string()
}
}
}
fn main() {
let responses = vec![
ApiResponse::Success("用戶資料".to_string()),
ApiResponse::ClientError(404, "用戶不存在".to_string()),
ApiResponse::ClientError(429, "速率限制,請稍候再試".to_string()),
ApiResponse::ServerError(500),
ApiResponse::NetworkError,
];
for response in responses {
println!("{}", handle_response(response));
}
}
當要開發大型專案時,mod 是不可或缺的,可以將複雜的程式模組拆分到各別的檔案中。 如果我們在同一個檔案中全使用 fn 拆分,可能會因為重複命名相同的函數,導致程式錯誤。
雖然我們之前說過,mod 可以分拆到不同檔案中,不過我們其實能在同一個程式中使用 mod。
底下是一個 mod 的使用範例,用來輸出數字的平方和立方。
要注意,不論是在同一個檔案或不同檔案,想在要 mod 區塊外使用內部的函數,就必須加上 pub。
mod math {
pub fn square(n: i32) -> i32 {
n * n
}
pub fn cube(n: i32) -> i32 {
n * n * n
}
}
fn main() {
println!("2 的平方是 {}", math::square(2));
println!("2 的立方是 {}", math::cube(2));
}
通常的 mod 使用方式,我們在同一個目錄下,拆分出一個新檔案 math.rs。
在 main.rs 使用 mod math ,來引用 math.rs 的內容。
// main.rs
mod math; // 引用 math.rs 檔案
fn main() {
println!("2 的平方是 {}", math::square(2));
println!("2 的立方是 {}", math::cube(2));
}
// math.rs
pub fn square(n: i32) -> i32 {
n * n
}
pub fn cube(n: i32) -> i32 {
n * n * n
}